package it.gmariotti.cardslib.library.view;

import it.gmariotti.cardslib.library.R;
import android.content.Context;
import android.os.Handler;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.Interpolator;
import android.view.animation.LinearInterpolator;
import android.view.animation.RotateAnimation;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView.OnItemLongClickListener;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;

/**
 * 类说明：
 * 
 * @author Toby.chen
 * @date 2013-3-29
 * @version 1.0
 */

public class PullToRefreshListView extends ListView implements OnScrollListener,
		OnItemClickListener, OnItemLongClickListener {

//	private static String TAG = "PullToRefreshListView";

	// 下拉刷新状态
	private final static int PULL_TO_RELEASE = 0;
	private final static int RELEASE_TO_REFRESH = 1;
	private final static int REFRESHING = 2;
	private final static int DONE = 3;

	private int mRefreshState = DONE;
	private boolean isOurControl = false;// 是否由自己的程序来控制listview的滚动，false表示由listView控制。

	private int mLastModifyY = 0;
	private int mFirstItemIndex = 0;

	private LinearLayout mHeaderView = null;
	private TextView mHeaderText = null;
	private ImageView mHeaderImage = null;
	private ProgressBar mHeaderProgress = null;
	private int mHeaderViewHeight;
	private RotateAnimation mAnimation;
	private RotateAnimation mReverseAnimation;
	private boolean isBackToPullRelease = false;

	private LinearLayout mFooterView = null;
	private boolean mIsFooterLoading = false;
	private boolean mIsFooterError = false;

	private SmoothScrollRunnable mCurrentSmoothScrollRunnable = null;
	private final Handler mHandler = new Handler();

	private RefreshingListener mRefreshListener = null;

	private boolean mIsHeaderNeedToRefresh = false;
	private boolean mIsFooterNeedToRefresh = false;
	private boolean mEnableRefreshHeaderAndFooterAtSameTime = false;

	private OnScrollListener mUserOnScrollListener = null;
	private OnItemClickListener mUserOnItemClickListener = null;
	private OnItemLongClickListener mUserOnItemLongClickListener = null;

	private boolean mShouldNotIntercept = false;
	private float mInterceptMotionX;
	private float mInterceptMotionY;

	public PullToRefreshListView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		init();
	}

	public PullToRefreshListView(Context context, AttributeSet attrs) {
		super(context, attrs);
		init();
	}

	public PullToRefreshListView(Context context) {
		super(context);
		init();
	}

	private String getString(int id) {
		return getResources().getString(id);
	}

	/**
	 * 下拉刷新完成后调用本函数
	 * */
	public void headerRefreshingCompleted() {
		resetStates();
		if (!mEnableRefreshHeaderAndFooterAtSameTime && mIsFooterNeedToRefresh) {
			mFooterView.setPadding(0, 0, 0, 0);
		}
	}

	/**
	 * 上拉刷新完成后调用本函数
	 * */
	public void footerRefreshingCompleted() {
		mIsFooterLoading = false;
	}

	public void footerRefreshError() {
		mIsFooterError = true;
		mIsFooterLoading = false;
		TextView footTextView = mFooterView.findViewById(R.id.pull_to_refresh_text);
		ImageView FooterImage = mFooterView.findViewById(R.id.pull_to_refresh_image);
		ProgressBar footerProgressBar = mFooterView
				.findViewById(R.id.pull_to_refresh_progress);

		footTextView.setText(getString(R.string.ptrl_refresh_fail));
		FooterImage.setVisibility(View.GONE);
		footerProgressBar.setVisibility(View.GONE);

		mFooterView.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				setFooterLoading();
				if (mRefreshListener != null && !mIsFooterLoading) {
					mIsFooterLoading = true;
					mRefreshListener.onFooterRefreshing();
				}
			}
		});
	}

	private void setFooterLoading() {
		mIsFooterError = false;
		TextView footTextView = mFooterView.findViewById(R.id.pull_to_refresh_text);
		ImageView FooterImage = mFooterView.findViewById(R.id.pull_to_refresh_image);
		ProgressBar footerProgressBar = mFooterView
				.findViewById(R.id.pull_to_refresh_progress);

		footTextView.setText(getString(R.string.ptr1_loading_wait));
		FooterImage.setVisibility(View.GONE);
		footerProgressBar.setVisibility(View.VISIBLE);
	}

	/**
	 * 设置刷新回调函数
	 * */
	public void setRefreshListener(RefreshingListener l) {
		mRefreshListener = l;
	}

	/**
	 * 是否需要顶部刷新功能，默认为true
	 * */
	public void needToRefreshOnHeader(boolean need) {
		mIsHeaderNeedToRefresh = need;
		resetStates();
	}

	/**
	 * 是否需要底部刷新功能，默认为true
	 * */
	public void needToRefreshOnFooter(boolean need) {
		mIsFooterNeedToRefresh = need;
		if (!need) {
			mFooterView.setPadding(0, 0, 0, -mHeaderViewHeight);
		} else {
			mFooterView.setPadding(0, 0, 0, 0);
		}
	}

	/**
	 * 底部刷新或顶部刷新能否同步进行，默认为false（同一时间只能刷新一个）
	 * */
	public void enableRefreshHeaderAndFooterAtSameTime(boolean enable) {
		mEnableRefreshHeaderAndFooterAtSameTime = enable;
	}

	public void startHeaderRefresh() {

		if (!mEnableRefreshHeaderAndFooterAtSameTime && mIsFooterLoading) {
			return;
		}

		setSelection(0);
		smoothScrollTo(0);
		mRefreshState = REFRESHING;
		changeHeaderViewByState();
		onHeaderRefreshing();
	}

	@Override
	public void setOnScrollListener(OnScrollListener l) {
		mUserOnScrollListener = l;
	}

	@Override
	public void setOnItemClickListener(OnItemClickListener l) {
		super.setOnItemClickListener(this);
		mUserOnItemClickListener = l;
	}

	@Override
	public void setOnItemLongClickListener(
			android.widget.AdapterView.OnItemLongClickListener listener) {
		super.setOnItemLongClickListener(this);
		mUserOnItemLongClickListener = listener;
	}

	@Override
	public void addFooterView(View v) {
		// 需要重载本函数，保证刷新使用footerview一定在最下边
		removeFooterView(mFooterView);
		super.addFooterView(v);
		// super.addFooterView(mFooterView, null, false);
		initFooterView(getContext());
	}

	@Override
	public void addFooterView(View v, Object data, boolean isSelectable) {
		// 需要重载本函数，保证刷新使用footerview一定在最下边
		removeFooterView(mFooterView);
		super.addFooterView(v, data, isSelectable);
		initFooterView(getContext());
	}

	private void init() {
		super.setOnScrollListener(this);
		initHeaderView(getContext());
		initFooterView(getContext());
	}

	private void initHeaderView(Context c) {
		mHeaderView = (LinearLayout) LayoutInflater.from(c).inflate(
				R.layout.refresh_list_loading_view, null);
		mHeaderText = mHeaderView.findViewById(R.id.pull_to_refresh_text);
		mHeaderImage = mHeaderView.findViewById(R.id.pull_to_refresh_image);
		mHeaderProgress = mHeaderView.findViewById(R.id.pull_to_refresh_progress);

		mHeaderText.setText(getString(R.string.ptrl_drag_to_refresh));
		mHeaderImage.setImageResource(R.drawable.pull_to_refresh_down_arrow);

		addHeaderView(mHeaderView, null, false);

		measureView(mHeaderView);
		mHeaderViewHeight = mHeaderView.getMeasuredHeight();
		mHeaderView.setPadding(0, -mHeaderViewHeight, 0, 0);

		mAnimation = new RotateAnimation(0, -180, RotateAnimation.RELATIVE_TO_SELF, 0.5f,
				RotateAnimation.RELATIVE_TO_SELF, 0.5f);
		mAnimation.setInterpolator(new LinearInterpolator());
		mAnimation.setDuration(250);
		mAnimation.setFillAfter(true);

		mReverseAnimation = new RotateAnimation(-180, 0, RotateAnimation.RELATIVE_TO_SELF, 0.5f,
				RotateAnimation.RELATIVE_TO_SELF, 0.5f);
		mReverseAnimation.setInterpolator(new LinearInterpolator());
		mReverseAnimation.setDuration(200);
		mReverseAnimation.setFillAfter(true);
		setHeaderDividersEnabled(true);
	}

	private void initFooterView(Context c) {
		mFooterView = (LinearLayout) LayoutInflater.from(c).inflate(
				R.layout.refresh_list_loading_view, null);
		TextView footTextView = mFooterView.findViewById(R.id.pull_to_refresh_text);
		ImageView FooterImage = mFooterView.findViewById(R.id.pull_to_refresh_image);
		ProgressBar footerProgressBar = mFooterView
				.findViewById(R.id.pull_to_refresh_progress);

		footTextView.setText(getString(R.string.ptr1_loading_wait));
		FooterImage.setVisibility(View.GONE);
		footerProgressBar.setVisibility(View.VISIBLE);

		super.addFooterView(mFooterView, null, false);
		setFooterDividersEnabled(true);
	}

	private void measureView(View child) {
		ViewGroup.LayoutParams p = child.getLayoutParams();
		if (p == null) {
			p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
					ViewGroup.LayoutParams.WRAP_CONTENT);
		}

		int childWidthSpec = ViewGroup.getChildMeasureSpec(0, 0 + 0, p.width);
		int lpHeight = p.height;
		int childHeightSpec;
		if (lpHeight > 0) {
			childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight, MeasureSpec.EXACTLY);
		} else {
			childHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
		}
		child.measure(childWidthSpec, childHeightSpec);
	}

	private void resetStates() {
		mHeaderView.setPadding(0, -mHeaderViewHeight, 0, 0);
		mRefreshState = DONE;
		isOurControl = false;
	}

	private void changeHeaderViewByState() {
		switch (mRefreshState) {
		case PULL_TO_RELEASE:
			mHeaderText.setText(getString(R.string.ptrl_drag_to_refresh));
			mHeaderImage.setVisibility(View.VISIBLE);
			mHeaderProgress.setVisibility(View.GONE);
			mHeaderImage.clearAnimation();
			if (isBackToPullRelease) {
				isBackToPullRelease = false;
				mHeaderImage.startAnimation(mReverseAnimation);
			}
			break;
		case RELEASE_TO_REFRESH:
			mHeaderText.setText(getString(R.string.ptrl_release_to_finish));
			mHeaderImage.setVisibility(View.VISIBLE);
			mHeaderProgress.setVisibility(View.GONE);
			mHeaderImage.clearAnimation();
			mHeaderImage.startAnimation(mAnimation);
			break;
		case REFRESHING:
			mHeaderText.setText(getString(R.string.ptrl_refreshing_please_wait));
			mHeaderImage.clearAnimation();
			mHeaderImage.setVisibility(View.GONE);
			mHeaderProgress.setVisibility(View.VISIBLE);
			break;
		case DONE:
			mHeaderText.setText(getString(R.string.ptrl_drag_to_refresh));
			mHeaderImage.setVisibility(View.VISIBLE);
			mHeaderProgress.setVisibility(View.GONE);
			mHeaderImage.clearAnimation();
			break;
		}
	}

	private void onHeaderRefreshing() {

		if (!mEnableRefreshHeaderAndFooterAtSameTime) {
			mFooterView.setPadding(0, 0, 0, -mHeaderViewHeight);
		}

		// 在外部刷新完成后，会调用刷新完成，如果外部刷新完成速度过快，会造成刷新结束后，smooth回弹没有结束，导致错误，
		// 所以在这个地方延迟进行刷新操作，保证smooth回弹结束后才刷新。
		mHandler.postDelayed(new DelayRefreshRunnable(), 300);
	}

	private final void smoothScrollTo(int y) {
		if (null != mCurrentSmoothScrollRunnable) {
			mCurrentSmoothScrollRunnable.stop();
		}

		int paddingTop = mHeaderView.getPaddingTop();

		if (mHeaderView.getPaddingTop() != y) {
			mCurrentSmoothScrollRunnable = new SmoothScrollRunnable(mHandler, paddingTop, y);
			mHandler.post(mCurrentSmoothScrollRunnable);
		}
	}

	@Override
	public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
			int totalItemCount) {

		if (mUserOnScrollListener != null) {
			mUserOnScrollListener
					.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);
		}

		mFirstItemIndex = firstVisibleItem;

		if (!mIsFooterNeedToRefresh) {
			return;
		}

		if (!mEnableRefreshHeaderAndFooterAtSameTime && mRefreshState == REFRESHING) {
			return;
		}

		if (mIsFooterError) {
			if (view.getLastVisiblePosition() < totalItemCount - 2) {
				setFooterLoading();
			}
			return;
		}

		if (view.getLastVisiblePosition() == totalItemCount - 1 && mRefreshListener != null
				&& !mIsFooterLoading) {
			mIsFooterLoading = true;
			mRefreshListener.onFooterRefreshing();
		}
	}

	@Override
	public void onScrollStateChanged(AbsListView view, int scrollState) {
		if (mUserOnScrollListener != null) {
			mUserOnScrollListener.onScrollStateChanged(view, scrollState);
		}
	}

	@Override
	public boolean onInterceptTouchEvent(MotionEvent ev) {
		final float x = ev.getX();
		final float y = ev.getY();
		final int action = ev.getAction();

		if (action == MotionEvent.ACTION_MOVE && mShouldNotIntercept) {
			return false;
		}

		switch (action) {
		case MotionEvent.ACTION_DOWN:
			mInterceptMotionX = x;
			mInterceptMotionY = y;
			mShouldNotIntercept = false;
			break;
		case MotionEvent.ACTION_MOVE:
			final int xDiff = (int) Math.abs(mInterceptMotionX - x);
			final int yDiff = (int) Math.abs(mInterceptMotionY - y);

			// 当在x轴方向上移动的距离大于Y方向时
			if (xDiff > yDiff) {
				mShouldNotIntercept = true;
			}

			mInterceptMotionX = x;
			mInterceptMotionY = y;
			break;
		}

		return super.onInterceptTouchEvent(ev);
	}

	@Override
	public boolean onTouchEvent(MotionEvent event) {
		if (!mIsHeaderNeedToRefresh) {
			return super.onTouchEvent(event);
		}

		if (!mEnableRefreshHeaderAndFooterAtSameTime && mIsFooterLoading) {
			return super.onTouchEvent(event);
		}

		int action = event.getAction();

		switch (action) {
		case MotionEvent.ACTION_DOWN:
			mLastModifyY = (int) event.getY();

			break;
		case MotionEvent.ACTION_MOVE:
			final int dy = mLastModifyY - (int) event.getY();
			mLastModifyY = (int) event.getY();

			// 当不是item0不在第一个时，不进行处理
			if (mFirstItemIndex == 0 && !isOurControl) {
				isOurControl = true;
			}

			if (!isOurControl) {
				break;
			}

			// 当是DONE状态，并且是往下拉的情况, 且在列表处于最顶上，则进入了PULL_TO_RELEASE状态
			if (mRefreshState == DONE && dy < 0 && mFirstItemIndex == 0) {
				mRefreshState = PULL_TO_RELEASE;
				isBackToPullRelease = false;
				changeHeaderViewByState();
			}

			if (mRefreshState == PULL_TO_RELEASE || mRefreshState == RELEASE_TO_REFRESH) {
				// 在PULL_TO_RELEASE或者RELEASE_TO_REFRESH状态下，mFirstItemIndex一定为0
				// 如果不为0，则需要对状态进行重置
				if (mFirstItemIndex != 0) {
					resetStates();
					changeHeaderViewByState();
					break;
				}

				// 在拉动过程当中，headerview的paddingTop可能成为一个很大负数，
				// 为了防止累计效应，这里控制，最大负数为mHeaderViewHeight
				if (mHeaderView.getPaddingTop() + mHeaderViewHeight < 0) {
					resetStates();
					changeHeaderViewByState();
					break;
				}

				// 当下拉刷新过程中，又往上移动，需要将selection设置为0，以抵消listview本身的滚动
				if (dy > 0) {
					setSelection(0);
				}

				if (mRefreshState == PULL_TO_RELEASE && mHeaderView.getPaddingTop() > 10) {// 10控制下来多少才进入刷新状态，越大下拉越多才能进入下来状态
					mRefreshState = RELEASE_TO_REFRESH;
					changeHeaderViewByState();
					break;
				} else if (mRefreshState == RELEASE_TO_REFRESH && mHeaderView.getPaddingTop() < 10) {
					mRefreshState = PULL_TO_RELEASE;
					isBackToPullRelease = true;
					changeHeaderViewByState();
					break;
				}

				// 通过修改headerview的paddingTop来实现下来超出
				int distance = mHeaderView.getPaddingTop() - dy / 2;
//				Log.e(TAG, "padding:" + distance);
				mHeaderView.setPadding(0, distance, 0, 0);
			}

			if (mRefreshState == REFRESHING) {
				// Log.i(TAG, "top:" + mHeaderView.getPaddingTop());
				if (mFirstItemIndex != 0) {
					mHeaderView.setPadding(0, 0, 0, 0);
					break;
				}
				int distance = mHeaderView.getPaddingTop() - dy / 2;
//				Log.e(TAG, "padding2:" + distance);
				mHeaderView.setPadding(0, distance, 0, 0);
			}
			break;
		case MotionEvent.ACTION_UP:
		case MotionEvent.ACTION_CANCEL:
			isOurControl = false;
			if (mRefreshState == PULL_TO_RELEASE) {
				smoothScrollTo(-mHeaderViewHeight);
				mRefreshState = DONE;
			} else if (mRefreshState == RELEASE_TO_REFRESH) {
				smoothScrollTo(0);
				mRefreshState = REFRESHING;
				changeHeaderViewByState();
				onHeaderRefreshing();
			} else if (mRefreshState == REFRESHING) {
				smoothScrollTo(0);
			}

			break;

		}
		return super.onTouchEvent(event);
	}

	final class DelayRefreshRunnable implements Runnable {

		@Override
		public void run() {
			if (mRefreshListener != null) {
				mRefreshListener.onHeaderRefreshing();
			}
		}
	}

	final class SmoothScrollRunnable implements Runnable {

		static final int ANIMATION_DURATION_MS = 190;
		static final int ANIMATION_FPS = 1000 / 60;

		private final Interpolator interpolator;
		private final int scrollToY;
		private final int scrollFromY;
		private final Handler handler;

		private boolean continueRunning = true;
		private long startTime = -1;
		private int currentY = -1;

		public SmoothScrollRunnable(Handler handler, int fromY, int toY) {
			this.handler = handler;
			this.scrollFromY = fromY;
			this.scrollToY = toY;
			this.interpolator = new AccelerateDecelerateInterpolator();
		}

		@Override
		public void run() {

			/**
			 * Only set startTime if this is the first time we're starting, else
			 * actually calculate the Y delta
			 */
			if (startTime == -1) {
				startTime = System.currentTimeMillis();
			} else {

				/**
				 * We do do all calculations in long to reduce software float
				 * calculations. We use 1000 as it gives us good accuracy and
				 * small rounding errors
				 */
				long normalizedTime = (1000 * (System.currentTimeMillis() - startTime))
						/ ANIMATION_DURATION_MS;
				normalizedTime = Math.max(Math.min(normalizedTime, 1000), 0);

				final int deltaY = Math.round((scrollFromY - scrollToY)
						* interpolator.getInterpolation(normalizedTime / 1000f));
				this.currentY = scrollFromY - deltaY;
				// Log.e(TAG, "padding3:" + currentY);
				mHeaderView.setPadding(0, this.currentY, 0, 0);
//				Log.e(TAG, "setPadding.......");
			}

			// If we're not at the target Y, keep going...
			if (continueRunning && scrollToY != currentY) {
				handler.postDelayed(this, ANIMATION_FPS);
			}
		}

		public void stop() {
			this.continueRunning = false;
			this.handler.removeCallbacks(this);
		}
	}

    public interface RefreshingListener {
		void onHeaderRefreshing();

		void onFooterRefreshing();
	}

	@Override
	public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
		if (mUserOnItemClickListener != null) {
			mUserOnItemClickListener.onItemClick(arg0, arg1, arg2 - 1, arg3);
		}

	}

	@Override
	public boolean onItemLongClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
		if (mUserOnItemLongClickListener != null) {
			return mUserOnItemLongClickListener.onItemLongClick(arg0, arg1, arg2 - 1, arg3);
		}
		return false;
	}

}
